package cn.xinfei.xdecision.data.thread;

import cn.xinfei.xdecision.common.model.datax.enums.ExecutorSceneEnum;
import cn.xinfei.xdecision.thread.CustomerThreadPoolExecutor;
import lombok.Data;
import org.apache.tomcat.util.descriptor.web.ContextHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestContextHolder;

import java.util.Date;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.*;


public class TraceThreadPoolExecutor extends ThreadPoolExecutor {

    private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());

    private Map<String, Date> startTimes;
    private Boolean independentTrace;
    private ExecutorSceneEnum executorEnum;
    private String poolName;


    public TraceThreadPoolExecutor(
            int corePoolSize,
            int maximumPoolSize,
            long keepAliveTime,
            TimeUnit unit,
            BlockingQueue<Runnable> workQueue,
            ThreadFactory threadFactory,
            RejectedExecutionHandler handler,
            Boolean independentTrace) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
        this.startTimes = new ConcurrentHashMap<>();
        this.independentTrace = independentTrace;
    }

    public TraceThreadPoolExecutor(
            int corePoolSize,
            int maximumPoolSize,
            long keepAliveTime,
            TimeUnit unit,
            BlockingQueue<Runnable> workQueue,
            ThreadFactory threadFactory,
            RejectedExecutionHandler handler,
            Boolean independentTrace,
            ExecutorSceneEnum executorEnum,
            String poolName) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
        this.startTimes = new ConcurrentHashMap<>();
        this.independentTrace = independentTrace;
        this.poolName = poolName;
        this.executorEnum = executorEnum;
    }

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        startTimes.put(String.valueOf(r.hashCode()), new Date());

    }

    @Override
    public void execute(Runnable command) {
        if (Objects.isNull(this.executorEnum)) {
            super.execute(wrapRunnable(command));
        } else {
            super.execute(wrapRunnable(command, this.executorEnum));
        }
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        Date startDate = startTimes.remove(String.valueOf(r.hashCode()));
        Date finishDate = new Date();
        long diff = finishDate.getTime() - startDate.getTime();
        LOGGER.info("{}-monitor:  Duration: {} ms,  PoolSize: {},  CorePoolSize: {}, Active: {}, Completed: {},  Task: {},  Queue: {},  LargestPoolSize: {},  MaximumPoolSize: {},   KeepAliveTime: {},  isShutdown: {},  isTerminated: {}",
                this.poolName,
                diff,
                this.getPoolSize(),
                this.getCorePoolSize(),
                this.getActiveCount(),
                this.getCompletedTaskCount(),
                this.getTaskCount(),
                this.getQueue().size(),
                this.getLargestPoolSize(),
                this.getMaximumPoolSize(),
                this.getKeepAliveTime(TimeUnit.MILLISECONDS),
                this.isShutdown(),
                this.isTerminated());
    }

    private Runnable wrapRunnable(Runnable command) {
        MdcTraceIdTaskDecorator mdcTraceIdTaskDecorator = new MdcTraceIdTaskDecorator(independentTrace);
        return mdcTraceIdTaskDecorator.decorate(command);
    }

    private Runnable wrapRunnable(Runnable command, ExecutorSceneEnum executorEnum) {
        MdcTraceIdTaskDecorator mdcTraceIdTaskDecorator = new MdcTraceIdTaskDecorator(independentTrace, executorEnum);
        return mdcTraceIdTaskDecorator.decorate(command);
    }

    public String getPoolName() {
        return poolName;
    }
}
